<!DOCTYPE html>
<html>
  <head>
    <!-- 渲染属性 -->
    <!-- <script src="node_modules/three/build/three.js"></script> -->
    <script type="importmap">
      {
        "imports": {
          "three": "./node_modules/three/build/three.module.js",
          "three/addons/": "./node_modules/three/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";

      // 引入dat.gui.js的一个类GUI
      import { GUI } from "three/addons/libs/lil-gui.module.min.js";
      // 浏览器控制台测试，是否引入成功
      console.log(THREE.Scene);
      console.log("完成");

      // 实例化一个gui对象
      const gui = new GUI();

      //改变交互界面style属性
      gui.domElement.style.right = "0px";
      gui.domElement.style.width = "300px";

      // three/addons/路径之后对应的是three.js官方文件包`/examples/jsm/`中的js库
      // 扩展库OrbitControls.js
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      // 扩展库GLTFLoader.js
      import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
      console.log(OrbitControls);
      console.log(GLTFLoader);

      const width = window.innerWidth;
      const height = window.innerHeight;
      // 开始创建
      // 设置场景,坐标原小 0，0，0 中场景中央
      const scene = new THREE.Scene();

      // 现在已经把几何体放到场景中了，但还需要一只眼睛
      // 这是一个透视摄像机
      // 第一个参数，视角，视角越大，看的东西越多，相对就越小。
      // 第二个参数，宽高比例
      // 第三个参数，当被观测物体的边界小于这个值时，不可见。，如果，在10 的位置，物体高为 10，那么物体就顶天立地
      // 第四个参数，当被观测物体的边界大于这个值时，不可见。
      const camera = new THREE.PerspectiveCamera(50, width / height, 10, 1000);

      // 设置摄像机的位置
      camera.position.set(-20, 15, 90);

      // 设置摄像机的观察点
      camera.lookAt(0, 0, 100);

      // 摄像机从 x -12 向 x 0 方向观察
      console.log(`camera`, camera.position);

      //camera.position.z=50

      // 设置几何体，几何体 x 方向宽度为 4 ，则x 负半轴为长度为 2，
      // 摄像机 x -12 , -12 - -2 =  10 为摄像机的近裁截面，所以这是能看见的最小距离
      const box = new THREE.BoxGeometry(4, 8, 2);

      // 设置材质 MeshBasicMaterial 基础材质不受光影响，得换一个

      const material = new THREE.MeshLambertMaterial({
        color: 0x0000ff,
        transparent: true, // 开启透明
        opacity: 0.5, // 透明度
      });

      // 创建网格模型
      const mesh = new THREE.Mesh(box, material);

      mesh.position.set(0, 0, 100);
      console.log(`mesh`, mesh.position);

      // 放到场景中
      scene.add(mesh);

      gui.add(mesh.position, "x", 0, 200).name("X轴坐标");
      gui.add(mesh.position, "y", 0, 200).step(0.1);
      gui.add(mesh.position, "z", 0, 200);

      // 创建子菜单
      const rotationFolder = gui.addFolder("角度");
      // 关闭，折叠菜单， open 是打开
      rotationFolder.close();

      rotationFolder.add(mesh.rotation, "x", 0, 3.14).name("X轴角度");
      rotationFolder
        .add(mesh.rotation, "y", 0, 3.14)
        .onChange(function (value) {
          console.log(`监听值变化：mesh.rotation.y`, value);
        });
      rotationFolder.add(mesh.rotation, "z", 0, 3.14);

      // 创建材质子菜单
      const matFolder = gui.addFolder("材质");
      //matFolder.close();
      const obj = {
        color: 0x00ffff,
        specular: 0x00ffff,
      };

      // .addColor()生成颜色值改变的交互界面
      matFolder.addColor(obj, "color").onChange(function (value) {
        // 颜色控件变化，调用方法
        mesh.material.color.set(value);
      });

      // 材质高光颜色specular,
      matFolder.addColor(obj, "specular").onChange(function (value) {
        // 如果没有 specular 会报错
        mesh.material.specular.set(value);
      });

      // 下拉菜单
      const selArr = {
        scale: 0,
      };
      // 参数3数据类型：数组(下拉菜单)
      gui
        .add(selArr, "scale", [-100, 0, 100])
        .name("y坐标")
        .onChange(function (value) {
          mesh.position.y = value;
        });

      // 对象下拉菜单
      const selObj = {
        scale: 0,
      };
      // 参数3数据类型：对象(下拉菜单)
      gui
        .add(selObj, "scale", {
          left: -100,
          center: 0,
          right: 100,
          // 左: -100,//可以用中文
          // 中: 0,
          // 右: 100
        })
        .name("位置选择")
        .onChange(function (value) {
          mesh.position.x = value;
        });

      // 布尔值
      const boolObj = {
        bool: false,
      };
      // 改变的obj属性数据类型是布尔值，交互界面是单选框
      gui
        .add(boolObj, "bool")
        .name("是否旋转")
        .onChange(function (value) {
          // 点击单选框，控制台打印obj.bool变化
          console.log("obj.bool", value);
        });



      // 点光源
      const pointLight = new THREE.PointLight(0xffffff, 1.0);
      pointLight.position.set(-50, 50, 80); //点光源放在x轴上

      scene.add(pointLight);

            // 菜单嵌套
      // 平行光子菜单
      const dirFolder = gui.addFolder("平行光");
      dirFolder.close(); //关闭菜单
      // 平行光强度
      dirFolder.add(pointLight, "intensity", 0, 2);
      const dirFolder2 = dirFolder.addFolder("位置"); //子菜单的子菜单
      dirFolder2.close(); //关闭菜单
      // 平行光位置
      dirFolder2.add(pointLight.position, "x", -400, 400);
      dirFolder2.add(pointLight.position, "y", -400, 400);
      dirFolder2.add(pointLight.position, "z", -400, 400);

      // 设置渲染器
      const renderer = new THREE.WebGLRenderer();

      renderer.setSize(width, height);

      // 设置空间背景颜色
      renderer.setClearColor(0xcccccc);
      // renderer.render(scene, camera);

      document.body.appendChild(renderer.domElement);

      // 渲染方法
      function render() {
        // 用渲染器渲染
        renderer.render(scene, camera);

        // 重复渲染，需要查一下
        // 请求动画帧
        // requestAnimationFrame实现周期性循环执行
        // requestAnimationFrame默认每秒钟执行60次，但不一定能做到，要看代码的性能
        requestAnimationFrame(render); // 请求再次执行函数render, 放上面下面都行
      }

      // 执行渲染方法
      render();
    </script>
  </head>
  <body>
    GUI 控件
    <div id="monitor"></div>
  </body>
</html>
